package net.flowas.genericcrud.service.chain;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import net.flowas.genericcrud.ExecuteContext;
import net.flowas.genericcrud.ResourceRegister;
import net.flowas.genericcrud.service.impl.CommonFunction;
import net.flowas.genericcrud.spi.ProcessListener;

public class ProcesserRegister implements ResourceRegister{
	private static final Logger LOG = LoggerFactory.getLogger(ProcesserRegister.class);
	private static final String CUSTOMER_PROCESSER_FILE = "META-INF/customerProcesser.xml";
	private Map<String, List<ProcessListener>> preProcessListener = new HashMap<>();
    private Map<String, List<ProcessListener>> postProcessListener = new HashMap<>();
    public Map<String, List<ProcessListener>> getPreProcessListener() {
        return preProcessListener;
      }
      public Map<String, List<ProcessListener>> getPostProcessListener() {
    	    return postProcessListener;
    	  }
      public void initDefaultListeners() {
    	try {
    		Enumeration<URL> urls = ProcesserRegister.class.getClassLoader().getResources(CUSTOMER_PROCESSER_FILE);
    		while(urls.hasMoreElements()) {
    	    	 URL url = urls.nextElement();
    	    	 addResource(url, ProcesserRegister.class.getClassLoader());	    	 
    	    }
    	} catch (IOException e1) {
    		LOG.warn("can not find CUSTOMER_PROCESSER_FILE file:[" + CUSTOMER_PROCESSER_FILE + "]",e1);
    	}	
      }

    private void parceElement(NodeList processers,Map<String, List<ProcessListener>> processListener,ClassLoader classLoader) {
    	 for (int i = 0;i < processers.getLength();i++) {
    	   Element preProcesser = (Element) processers.item(i);
    	   String forModleType = preProcesser.getAttribute("forModleType");
    	   String onMethod = preProcesser.getAttribute("onMethod");
    	   String clasz = preProcesser.getAttribute("class");
    	   String[] methods;
    	   if(onMethod.contains(",")) {
    		   methods=onMethod.split(",");
    	   }else {
    		   methods = new String[] {onMethod};
    	   }	   
    	   try {
    		   ProcessListener processer = (ProcessListener) classLoader.loadClass(clasz).getDeclaredConstructor().newInstance();
    		   for (int j = 0; j < methods.length; j++) {
    			 String key = methods[j] + "_" + forModleType;			 
    		     List<ProcessListener> listenerArray = processListener.get(key);
    		     if (listenerArray == null) {
    		       listenerArray = new ArrayList<>();
    		       processListener.put(key, listenerArray);
    		     }
    		     listenerArray.add(processer);
    		   }  
    	   } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SecurityException
    			   |IllegalArgumentException | InvocationTargetException | NoSuchMethodException e) {
    	     LOG.error("failed to instance ProcessListener[" + clasz + "]", e);
    	   } 
    	 }
       }

    	@Override
    	public void addResource(URL url, ClassLoader classLoader) {
    		Element processers = CommonFunction.parceXmlConfig(url);
       	    NodeList preProcessers = processers.getElementsByTagName("preProcesser");
            parceElement(preProcessers,preProcessListener,classLoader);
            NodeList postProcessers = processers.getElementsByTagName("postProcesser");
            parceElement(postProcessers,postProcessListener,classLoader);
    	}
    	
    	@Override
    	public void removeResource(URL url) {
    		// TODO Auto-generated method stub
    	}
		public void preProcess(ExecuteContext req) {
			List<ProcessListener> prelisteners = getPreProcessListener().get(req.getAction().name()+"_" + req.getModelType());
		    if (prelisteners != null) {
		      for (ProcessListener processListener : prelisteners) {
		        processListener.process(req);
		      }
		    }
		}
		public void postProcess(ExecuteContext req) {
			List<ProcessListener> postlisteners = getPostProcessListener().get(req.getAction().name()+"_" + req.getModelType());
		    if (postlisteners != null) {
		      for (ProcessListener processListener : postlisteners) {
		        processListener.process(req);
		      }
		    }
		}
}
